/*
 * C3.3.8.c
 *
 *  Created on: 2013/01/22
 *      Author: xun
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rule_info.h"
#include "core.h"
#include "trace.h"
#include "TraceAst.h"

static const PSZ RuleID = "C3.3.8";

//TODO
static const PSZ MESSAGE_ENG = "";
static const PSZ MESSAGE_JPN = "明示的なreturn文がない。";
static const PSZ MESSAGE_CHS = "";

static const U2 RANK = MSG_RANK_ERR;

static PRULEOUT pRuleHead;
static PRULEOUT pRuleTail;

static INT RuleCheck(U2 kind, AST index, U4 event, AST* pCodeScp);
static INT StatCheck(AST index, AST* pCodeScp);

/******************************************************************
 * main logic
 *******************************************************************/
static S4 RuleMain(U2 kind, AST index, U4 event) {
	INT result = RULE_RESULT_OK;
	PRULEOUT newNode = NULL;
	PRULEOUT tmpNode = NULL;
	PRULEOUT tmpNode1 = NULL;

	AST codeScp = 0;

	//TODO
	result = RuleCheck(kind, index, event, &codeScp);

	if (result == RULE_RESULT_OK) {
		return EXIT_SUCCESS;
	}

	/*re-checked?*/
	tmpNode = pRuleHead;
	while (tmpNode != NULL ) {
		if (tmpNode->indexInfo == index) {
			if (result > RULE_RESULT_OK) {
				result = RULE_RESULT_OK;
			} else {
				if (tmpNode == pRuleHead) {
					pRuleHead = pRuleHead->next;
					free(tmpNode);
				} else if (tmpNode == pRuleTail) {
					tmpNode1->next = NULL;
					pRuleTail = tmpNode1;
					free(tmpNode);
				} else {
					tmpNode1->next = tmpNode->next;
					free(tmpNode);
				}
			}
			break;
		}
		tmpNode1 = tmpNode;
		tmpNode = tmpNode->next;
	}

	/*make result struct*/
	if (result > RULE_RESULT_OK) {
		newNode = (PRULEOUT) malloc(sizeof(RULEOUT));
		if (pRuleHead == NULL ) {
			pRuleHead = newNode;
		}

		//TODO
		//codeScp = ??;
		newNode->indexInfo = index;
		newNode->fileIndex = CCodeScope.getFileIndex(codeScp);
		newNode->beginLine = CCodeScope.getBeginLine(codeScp);
		newNode->beginCol = CCodeScope.getBeginLineOffset(codeScp);
		newNode->endLine = CCodeScope.getEndLine(codeScp);
		newNode->endCol = CCodeScope.getEndLineOffset(codeScp);
		newNode->ruleID = RuleID;
		newNode->errRank = RANK;

		//TODO
		sprintf(newNode->msgBuf_eng, MESSAGE_ENG);
		sprintf(newNode->msgBuf_chs, MESSAGE_CHS);
		sprintf(newNode->msgBuf_jpn, MESSAGE_JPN);

		newNode->rfileIndex = 0;
		newNode->rlocLine = 0;
		newNode->next = NULL;

		if (pRuleTail != NULL ) {
			pRuleTail->next = newNode;
		}

		pRuleTail = newNode;
	}

	return EXIT_SUCCESS;
}

VOID C3_3_8_register(VOID) {
	PRULEMAN * pCurRule = &stRuleManager;
	PRULEMAN newRuleMan = (PRULEMAN) malloc(sizeof(RULEMAN));
	memset(newRuleMan, 0, sizeof(RULEMAN));
	newRuleMan->ruleID = RuleID;
	newRuleMan->ruleSwitch = OFF;
	newRuleMan->headOut = &pRuleHead;
	newRuleMan->tailOut = &pRuleTail;
	newRuleMan->ruleMain_ast = RuleMain;
	if (stRuleManager == NULL ) {
		stRuleManager = newRuleMan;
	} else {
		while ((*pCurRule)->nextRule != NULL ) {
			pCurRule = &((*pCurRule)->nextRule);
		}
		(*pCurRule)->nextRule = newRuleMan;
	}
}

static INT RuleCheck(U2 kind, AST index, U4 event, AST* pCodeScp) {
	INT result = RULE_RESULT_OK;

	AST astDeclarator = 0;
	AST astTypeSpec = 0;
	AST astFunctionSymbol = 0;
	AST astBlockStat = 0;

	if (kind != TR_KIND_DECLARATION) {
		return EXIT_SUCCESS;
	}

	if (!(event & EVT_DECLARATION)) {
		return EXIT_SUCCESS;
	}

	astDeclarator = CDeclaration.getDeclarator(index);
	if (astDeclarator != 0 && CDeclarator.isFunctionDeclarator(astDeclarator)) {
		// if TypeSpecifier is void , return without check
		astTypeSpec = CDeclaration.getTypeSpecifier(index);
		if (CTypeSpecifier.getTypeInfo(astTypeSpec) == 1) {
			return EXIT_SUCCESS;
		}
		astDeclarator = CDeclarator.getChildDeclarator(astDeclarator);
		while(astDeclarator != 0 && !CDeclarator.isSymbolDeclarator(astDeclarator)){
			astDeclarator = CDeclarator.getChildDeclarator(astDeclarator);
		}
		if (astDeclarator != 0
				&& CDeclarator.isSymbolDeclarator(astDeclarator)) {
			astFunctionSymbol = CDeclarator.symbol_getSymbol(astDeclarator);
			if (astFunctionSymbol != 0
					&& CSymbol.isFunction(astFunctionSymbol)) {
				astBlockStat = CSymbol.getFunctionBody(astFunctionSymbol);
				result = StatCheck(astBlockStat, pCodeScp);
			}
		}
	}

	return result;
}

static INT StatCheck(AST index, AST* pCodeScp) {
	INT result = RULE_RESULT_NG;

	AST astStat = 0;
	AST astNextStat = 0;
	AST astExpr = 0;

	if (index == 0) {
		*pCodeScp = 0;
		return result;
	}

	if (CStatement.isBlock(index)) {
		astStat = CStatement.block_getChain(index);
		astNextStat = astStat;
		while (astNextStat != 0 && !CStatement.isBreak(astNextStat)
				&& !CStatement.isContinue(astNextStat)) {
			astStat = astNextStat;
			*pCodeScp = 0;
			if (StatCheck(astStat, pCodeScp) == RULE_RESULT_OK) {
				return RULE_RESULT_OK;
			}
			astNextStat = CStatement.getNextStmt(astStat);
		}

		if (astNextStat == 0 && *pCodeScp == 0 && astStat != 0) {
			*pCodeScp = CStatement.getCodeScope(astStat);
		} else if (astNextStat == 0 && *pCodeScp == 0 && astStat == 0) {
			*pCodeScp = CStatement.getCodeScope(index);
		} else if (astNextStat != 0 && *pCodeScp == 0) {
			*pCodeScp = CStatement.getCodeScope(astNextStat);
		}
		return RULE_RESULT_NG;

	} else if (CStatement.isBreak(index)) {
		// do nothing
	} else if (CStatement.isCase(index)) {
		astStat = CStatement.case_getStatement(index);
		if (StatCheck(astStat, pCodeScp) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}
		astNextStat = CStatement.getNextStmt(index);
		while (astNextStat != 0 && !CStatement.isCase(astNextStat)
				&& !CStatement.isDefault(astNextStat)
				&& !CStatement.isBreak(astNextStat)
				&& !CStatement.isContinue(astNextStat)) {
			astStat = astNextStat;
			*pCodeScp = 0;
			if (StatCheck(astStat, pCodeScp) == RULE_RESULT_OK) {
				return RULE_RESULT_OK;
			}
			astNextStat = CStatement.getNextStmt(astStat);
		}
		if (astNextStat == 0 && *pCodeScp == 0 && astStat != 0) {
			*pCodeScp = CStatement.getCodeScope(astStat);
		} else if (astNextStat == 0 && *pCodeScp == 0 && astStat == 0) {
			*pCodeScp = CStatement.getCodeScope(index);
		} else if (astNextStat != 0 && *pCodeScp == 0) {
			*pCodeScp = CStatement.getCodeScope(astNextStat);
		}
		return RULE_RESULT_NG;

	} else if (CStatement.isContinue(index)) {
		// do nothing
	} else if (CStatement.isDecl(index)) {
		// do nothing
	} else if (CStatement.isDefault(index)) {
		astStat = CStatement.default_getStatement(index);
		if (StatCheck(astStat, pCodeScp) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		}
		astNextStat = CStatement.getNextStmt(index);
		while (astNextStat != 0 && !CStatement.isCase(astNextStat)
				&& !CStatement.isDefault(astNextStat)
				&& !CStatement.isBreak(astNextStat)
				&& !CStatement.isContinue(astNextStat)) {
			astStat = astNextStat;
			*pCodeScp = 0;
			if (StatCheck(astStat, pCodeScp) == RULE_RESULT_OK) {
				return RULE_RESULT_OK;
			}
			astNextStat = CStatement.getNextStmt(astStat);
		}
		if (astNextStat == 0 && *pCodeScp == 0 && astStat != 0) {
			*pCodeScp = CStatement.getCodeScope(astStat);
		} else if (astNextStat == 0 && *pCodeScp == 0 && astStat == 0) {
			*pCodeScp = CStatement.getCodeScope(index);
		} else if (astNextStat != 0 && *pCodeScp == 0) {
			*pCodeScp = CStatement.getCodeScope(astNextStat);
		}
		return RULE_RESULT_NG;
	} else if (CStatement.isDo(index)) {
		astStat = CStatement.do_getBody(index);
		if (StatCheck(astStat, pCodeScp) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		} else if (*pCodeScp == 0) {
			*pCodeScp = CStatement.getCodeScope(index);
		}
		return RULE_RESULT_NG;
	} else if (CStatement.isExpression(index)) {
		// do nothing
	} else if (CStatement.isFor(index)) {
		astStat = CStatement.for_getBody(index);
		if (StatCheck(astStat, pCodeScp) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		} else if (*pCodeScp == 0) {
			*pCodeScp = CStatement.getCodeScope(index);
		}

	} else if (CStatement.isGoto(index)) {
		// TODO
	} else if (CStatement.isIf(index)) {
		astStat = CStatement.if_getBody(index);
		if (StatCheck(astStat, pCodeScp) == RULE_RESULT_NG) {
			if (*pCodeScp == 0) {
				*pCodeScp = CStatement.getCodeScope(index);
			}
			return RULE_RESULT_NG;
		}
		astStat = CStatement.if_getElseBody(index);
		if (StatCheck(astStat, pCodeScp) == RULE_RESULT_NG) {
			if (*pCodeScp == 0) {
				*pCodeScp = CStatement.getCodeScope(index);
			}
			return RULE_RESULT_NG;
		} else {
			return RULE_RESULT_OK;
		}
	} else if (CStatement.isLabel(index)) {
		astStat = CStatement.label_getStatement(index);
		if (StatCheck(astStat, pCodeScp) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		} else if (*pCodeScp == 0) {
			*pCodeScp = CStatement.getCodeScope(index);
		}
	} else if (CStatement.isReturn(index)) {
		astExpr = CStatement.return_getExp(index);
		if (astExpr != 0) {
			return RULE_RESULT_OK;
		} else {
			*pCodeScp = CStatement.getCodeScope(index);
			return RULE_RESULT_NG;
		}
	} else if (CStatement.isSwitch(index)) {
		// there should be a return in all the case and default
		astStat = CStatement.switch_getDefault(index);
		if (astStat == 0 || StatCheck(astStat, pCodeScp) == RULE_RESULT_NG) {
			if (*pCodeScp == 0) {
				*pCodeScp = CStatement.getCodeScope(index);
			}
			return RULE_RESULT_NG;
		}
		astStat = CStatement.switch_getFirstCase(index);
		while (astStat != 0) {
			if (StatCheck(astStat, pCodeScp) == RULE_RESULT_NG) {
				if (*pCodeScp == 0) {
					*pCodeScp = CStatement.getCodeScope(index);
				}
				return RULE_RESULT_NG;
			}
			astStat = CStatement.case_getNextCase(astStat);
		}
		if (astStat == 0) {
			return RULE_RESULT_OK;
		}
	} else if (CStatement.isWhile(index)) {
		astStat = CStatement.while_getBody(index);
		if (StatCheck(astStat, pCodeScp) == RULE_RESULT_OK) {
			return RULE_RESULT_OK;
		} else if (*pCodeScp == 0) {
			*pCodeScp = CStatement.getCodeScope(index);
		}
	}

	if (*pCodeScp == 0) {
		*pCodeScp = CStatement.getCodeScope(index);
	}
	return result;
}
